#!/usr/bin/python3
"""Fetch OSTree commits from an repository

Uses ostree to pull specific commits from (remote) repositories
at the provided `url`. Can verify the commit, if one or more
gpg keys are provided via `gpgkeys`.
"""


import os
import sys
import subprocess
import uuid

from osbuild import sources


SCHEMA = """
"additionalProperties": false,
"definitions": {
  "item": {
    "description": "The commits to fetch indexed their checksum",
    "type": "object",
    "additionalProperties": false,
    "patternProperties": {
      "[0-9a-f]{5,64}": {
        "type": "object",
        "additionalProperties": false,
        "required": ["remote"],
        "properties": {
          "remote": {
            "type": "object",
            "additionalProperties": false,
            "required": ["url"],
            "properties": {
              "url": {
                "type": "string",
                "description": "URL of the repository."
              },
              "gpgkeys": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "GPG keys to verify the commits"
                }
              }
            }
          }
        }
      }
    }
  }
},
"properties": {
  "items": {"$ref": "#/definitions/item"},
  "commits": {"$ref": "#/definitions/item"}
},
"oneOf": [{
  "required": ["items"]
}, {
  "required": ["commits"]
}]
"""


def ostree(*args, _input=None, **kwargs):
    args = list(args) + [f'--{k}={v}' for k, v in kwargs.items()]
    print("ostree " + " ".join(args), file=sys.stderr)
    subprocess.run(["ostree"] + args,
                   encoding="utf-8",
                   stdout=subprocess.PIPE,
                   stderr=subprocess.STDOUT,
                   input=_input,
                   check=True)


def download(items, cache):
    # Prepare the cache and the output repo
    repo_cache = os.path.join(cache, "repo")
    ostree("init", mode="archive", repo=repo_cache)

    # Make sure the cache repository uses locks to protect the metadata during
    # shared access. This is the default since `2018.5`, but lets document this
    # explicitly here.
    ostree("config", "set", "repo.locking", "true", repo=repo_cache)

    for commit, item in items.items():
        remote = item["remote"]
        url = remote["url"]
        gpg = remote.get("gpgkeys", [])
        uid = str(uuid.uuid4())

        verify_args = []
        if not gpg:
            verify_args = ["--no-gpg-verify"]

        ostree("remote", "add",
               uid, url,
               *verify_args,
               repo=repo_cache)

        for key in gpg:
            ostree("remote", "gpg-import", "--stdin", uid,
                   repo=repo_cache, _input=key)

        # Transfer the commit: remote → cache
        print(f"pulling {commit}", file=sys.stderr)
        ostree("pull", uid, commit, repo=repo_cache)

        # Remove the temporary remotes again
        ostree("remote", "delete", uid,
               repo=repo_cache)


class OSTreeSource(sources.SourceService):

    def download(self, items, cache, _options):
        cache = os.path.join(cache, "org.osbuild.ostree")
        os.makedirs(cache, exist_ok=True)

        download(items, cache)


def main():
    service = OSTreeSource.from_args(sys.argv[1:])
    service.main()


if __name__ == '__main__':
    main()
